Why code that works once isn't code you can trust — and how pytest changes that
Day 66 of 80
Every time you want to check if your code works, you probably do something like this:
That works when you have 50 lines of code. It breaks down fast when you have 500 — or when you change something in Week 15 and discover you quietly broke something from Week 11 that you haven't touched since.
Manual testing doesn't scale. As the DVP Prompt Vault grows — more commands, more edge cases, more platforms — you cannot manually verify every path every time you make a change. One missed edge case ships as a bug.
Automated tests are code that checks other code. You write them once, run them in a single command, and instantly know if anything broke. The test suite is your safety net — it lets you refactor, extend, and improve with confidence.
No matter how complex, every test follows the same three-step structure:
This pattern is sometimes called Arrange / Act / Assert (AAA). Keep it in your head — every test you write for the rest of this course follows it.
def test_platform_is_valid():
# 1. Arrange — set up the data
platform = "Kling"
valid_platforms = ["Kling", "Runway", "Veo"]
# 2. Act — run the code being tested
result = platform in valid_platforms
# 3. Assert — check the result
assert result is True
| Resource | Length | Focus |
|---|---|---|
| Corey Schafer — Python Unit Testing with pytest | ~40 min | Writing real test functions, running pytest, reading output |
Don't just watch passively. Pause when Corey writes a test function and type it yourself. Run it. Break it on purpose and watch the output. The failure message is your first real debugging tool in pytest.
After watching, skim the pytest section of this article: Real Python — Getting Started with Testing in Python. Focus on the sections about pytest specifically, not unittest.
pip install pytest
Verify it installed correctly:
pytest --version
pytest 8.x.x. If you get "command not found", your virtual environment may not be active.Python ships with a built-in testing module called unittest. You'll see it in older codebases. You don't need to use it. Here's why pytest is better for this course:
| Feature | unittest | pytest |
|---|---|---|
| Requires a class? | Yes (class TestFoo(unittest.TestCase)) |
No — plain functions work |
| Assertions | self.assertEqual(a, b) |
assert a == b |
| Test discovery | Verbose setup | Auto-discovers any test_*.py file |
| Fixtures | Complicated | Clean, composable with @pytest.fixture |
| Output | Minimal | Rich, color-coded, shows exact failure details |
# unittest — requires a class, special assertions
import unittest
class TestMath(unittest.TestCase):
def test_addition(self):
self.assertEqual(1 + 1, 2)
# pytest — just a function
def test_addition():
assert 1 + 1 == 2
assert is a Python keyword. It evaluates an expression, and if the expression is False, it raises an AssertionError and the test fails. If it's True, nothing happens — the test continues (and eventually passes).
You can add a message to describe what went wrong:
# Basic equality
assert result == "Kling"
# With a helpful message (shown on failure)
assert result == "Kling", f"Expected 'Kling', got '{result}'"
# Membership test
assert "aerial" in shots
# Length check
assert len(shots) == 3
# Truthiness
assert results # passes if list is non-empty
# Not equal
assert platform != "Pika"
You can have multiple asserts in one test, but each test should check one logical thing. If a test has 10 asserts, consider splitting it. When it fails, you want to know immediately what broke — not wade through 10 checks to find the one that failed.
Run pytest from your project root and it automatically finds all test files. The rules:
| Rule | Example |
|---|---|
Files named test_*.py or *_test.py |
test_models.py, api_test.py |
Functions starting with test_ |
def test_platform_valid(): |
Classes starting with Test (optional) |
class TestPrompt: |
If your file is called my_checks.py and your function is called check_platform(), pytest will not find it. The test_ prefix is the convention. Follow it exactly.
$ pytest test_basics.py -v
test_basics.py::test_addition PASSED [ 25%]
test_basics.py::test_string_upper PASSED [ 50%]
test_basics.py::test_list_append PASSED [ 75%]
test_basics.py::test_dictionary_access PASSED [100%]
=================== 4 passed in 0.02s ===================
-v flag (verbose) shows each test name. Without it you just see dots. Use -v when learning — it's much clearer.$ pytest test_basics.py -v
test_basics.py::test_addition FAILED [ 25%]
========================= FAILURES =========================
____________________ test_addition ____________________
def test_addition():
> assert 1 + 1 == 3
E assert 2 == 3
test_basics.py:2: AssertionError
=================== 1 failed, 3 passed in 0.05s ===================
pytest --versionassert works and how pytest reports failuresDay 67 is hands-on. You'll write your first real test file — test_basics.py — with tests for strings, lists, and dictionaries. Then you'll see both green (passing) and red (failing) output. You'll also learn pytest.raises() for testing errors.